#if ENABLE_PLAYFABPLAYSTREAM_API && ENABLE_PLAYFABSERVER_API
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using SignalR.Client._20.Transports;
using SignalR.Infrastructure;

namespace SignalR.Client._20.Http
{
    internal static class HttpHelper
    {
        public static EventSignal<CallbackDetail<HttpWebResponse>> PostAsync(string url)
        {
            return PostInternal(url, null, null);
        }

        public static void PostAsync(string url, IDictionary<string, string> postData)
        {
            PostInternal(url, null, postData);
        }

        public static EventSignal<CallbackDetail<HttpWebResponse>> PostAsync(
            string url,
            Action<HttpWebRequest> requestPreparer)
        {
            return PostInternal(url, requestPreparer, null);
        }

        public static EventSignal<CallbackDetail<HttpWebResponse>> PostAsync(
            string url,
            Action<HttpWebRequest> requestPreparer,
            IDictionary<string, string> postData)
        {
            return PostInternal(url, requestPreparer, postData);
        }

        public static string ReadAsString(HttpWebResponse response)
        {
            try
            {
                using (response)
                {
                    using (var stream = response.GetResponseStream())
                    {
                        using (var reader = new StreamReader(stream))
                        {
                            return reader.ReadToEnd();
                        }
                    }
                }
            }
            catch
            {
                return null;
            }
        }

        private static EventSignal<CallbackDetail<HttpWebResponse>> PostInternal(
            string url,
            Action<HttpWebRequest> requestPreparer,
            IDictionary<string, string> postData)
        {
            var _request = (HttpWebRequest)HttpWebRequest.Create(url);

            if (requestPreparer != null)
                requestPreparer(_request);

            var buffer = ProcessPostData(postData);

            _request.Method = "POST";
            _request.ContentType = "application/x-www-form-urlencoded";
            // Set the content length if the buffer is non-null
            _request.ContentLength = buffer != null ? buffer.LongLength : 0;

            EventSignal<CallbackDetail<HttpWebResponse>> _signal =
                new EventSignal<CallbackDetail<HttpWebResponse>>();

            if (buffer == null)
            {
                // If there's nothing to be written to the request then just get the response
                GetResponseAsync(_request, _signal);
                return _signal;
            }

            RequestState _requestState = new RequestState
            {
                PostData = buffer,
                Request = _request,
                Response = _signal
            };

            try
            {
                _request.BeginGetRequestStream(GetRequestStreamCallback, _requestState);
            }
            catch (Exception ex)
            {
                _signal.OnFinish(new CallbackDetail<HttpWebResponse> { IsFaulted = true, Exception = ex });
            }
            return _signal;
        }

        public static EventSignal<CallbackDetail<HttpWebResponse>> GetAsync(string url)
        {
            return GetAsync(url, null);
        }

        public static EventSignal<CallbackDetail<HttpWebResponse>> GetAsync(string url, Action<HttpWebRequest> requestPreparer)
        {
            var _request = (HttpWebRequest)HttpWebRequest.Create(url);
            ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
            if (requestPreparer != null)
            {
                requestPreparer(_request);
            }
            var signal = new EventSignal<CallbackDetail<HttpWebResponse>>();
            GetResponseAsync(_request, signal);
            return signal;
        }

        private static bool MyRemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            bool isOk = true;
            // If there are errors in the certificate chain,
            // look at each error to determine the cause.
            if (sslPolicyErrors != SslPolicyErrors.None)
            {
                for (int i = 0; i < chain.ChainStatus.Length; i++)
                {
                    if (chain.ChainStatus[i].Status == X509ChainStatusFlags.RevocationStatusUnknown)
                    {
                        continue;
                    }
                    chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
                    chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
                    chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
                    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
                    bool chainIsValid = chain.Build((X509Certificate2)certificate);
                    if (!chainIsValid)
                    {
                        isOk = false;
                        break;
                    }
                }
            }
            return isOk;
        }

        public static void GetResponseAsync(HttpWebRequest request, EventSignal<CallbackDetail<HttpWebResponse>> signal)
        {
            try
            {
                request.BeginGetResponse(
                    GetResponseCallback,
                    new RequestState
                    {
                        Request = request,
                        PostData = new byte[] { },
                        Response = signal
                    });
            }
            catch (Exception ex)
            {
                signal.OnFinish(new CallbackDetail<HttpWebResponse> { Exception = ex, IsFaulted = true });
            }
        }

        private static void GetRequestStreamCallback(IAsyncResult asynchronousResult)
        {
            RequestState _requestState = (RequestState)asynchronousResult.AsyncState;

            // End the operation
            try
            {
                Stream _postStream = _requestState.Request.EndGetRequestStream(asynchronousResult);

                // Write to the request stream.
                _postStream.Write(_requestState.PostData, 0, _requestState.PostData.Length);
                _postStream.Close();
            }
            catch (WebException exception)
            {
                _requestState.Response.OnFinish(new CallbackDetail<HttpWebResponse>
                {
                    IsFaulted = true,
                    Exception = exception
                });
                return;
            }

            // Start the asynchronous operation to get the response
            _requestState.Request.BeginGetResponse(GetResponseCallback, _requestState);
        }

        private static void GetResponseCallback(IAsyncResult asynchronousResult)
        {
            RequestState _requestState = (RequestState)asynchronousResult.AsyncState;
            // End the operation
            try
            {
                HttpWebResponse _response = (HttpWebResponse)_requestState.Request.EndGetResponse(asynchronousResult);
                _requestState.Response.OnFinish(new CallbackDetail<HttpWebResponse>
                {
                    Result = _response
                });
            }
            catch (Exception ex)
            {
                UnityEngine.Debug.LogException(ex);
                _requestState.Response.OnFinish(new CallbackDetail<HttpWebResponse> { IsFaulted = true, Exception = ex });
            }
        }

        private static byte[] ProcessPostData(IDictionary<string, string> postData)
        {
            if (postData == null || postData.Count == 0)
                return null;

            var _stringB = new StringBuilder();
            foreach (var pair in postData)
            {
                if (_stringB.Length > 0)
                    _stringB.Append("&");

                if (String.IsNullOrEmpty(pair.Value))
                    continue;
                _stringB.AppendFormat("{0}={1}", pair.Key, UriQueryUtility.UrlEncode(pair.Value));
            }
            return Encoding.UTF8.GetBytes(_stringB.ToString());
        }
    }
}
#endif
